package modele;


import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.util.Version;
import org.hibernate.Criteria;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;

@Stateless
public class FacadeSite {

    @PersistenceContext
    private EntityManager em;
  
    
    public HashMap<String, Long> getIdsSports(){
    	
    	// On va récupérer les ids des sports avec leurs noms depuis la BDD
    	CriteriaBuilder builder = em.getCriteriaBuilder();
    	CriteriaQuery<Sport> criteria = builder.createQuery(Sport.class);
    	Root<Sport> sportRoot = criteria.from(Sport.class);
    	
    	criteria.select(sportRoot);
		
		ArrayList<Sport> sports = (ArrayList<Sport>) em.createQuery(criteria).getResultList();
		
		HashMap<String, Long> idsSports = new HashMap<String, Long>();
		
		for(Sport s : sports){
			idsSports.put(s.getName(), s.getId());
		}
		
		return idsSports;
    }
    
    public List<Competition> getCompetitions(long idSport){
    	
    	// Récupération des compétitions depuis la BDD
    	CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<Competition> criteria = builder.createQuery(Competition.class);
		Root<Competition> competitionRoot = criteria.from(Competition.class);
		
		criteria.select(competitionRoot);
		criteria.where(builder.equal(competitionRoot.get("sport"), idSport));
		
		return em.createQuery(criteria).getResultList();
    }
    
    public List<Sport> getSports(){
    	
    	// Récupération des sports depuis la BDD
    	CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<Sport> criteria = builder.createQuery(Sport.class);
		Root<Sport> sportRoot = criteria.from(Sport.class);
		
		criteria.select(sportRoot);
		
		return em.createQuery(criteria).getResultList();
    }
    
    // Récupération d'un article depuis son id
    public Article getArticle(long id) {
		return em.find(Article.class, id);
    }
    
    public int getNombreResultatsRechercheArticles(Long idSport, Long idCompetition, 
    		Long idAuteur, String titre, Date dateMinimum, Float noteMinimum, Integer nombreCommentairesMinimum) {
    	
    	CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<Article> criteria = builder.createQuery(Article.class);
		Root<Article> articleRoot = criteria.from(Article.class);

		criteria.select(articleRoot);
		
		Predicate condition = genererCondition(articleRoot, idSport, idCompetition, idAuteur, dateMinimum, noteMinimum, nombreCommentairesMinimum);
		
		if (condition != null) {		
			criteria.where(condition);
		}
		TypedQuery<Article> query = em.createQuery(criteria);
		return  query.getResultList().size();
    	
    }
    /**
     * Recherche d'articles à partir de divers paramètres (qui peuvent être mis à null)
     * Un système de pagination est permis grâce à un offset et un nombre max de résultats par page
     * @param offsetResultats
     * @param nombreResultatsMax
     * @param attributTri
     * @param triAscendant
     * @param idSport
     * @param idCompetition
     * @param idAuteur
     * @param titre
     * @param dateMinimum
     * @param noteMinimum
     * @param nombreCommentairesMinimum
     * @return liste d'articles
     */
    public List<Article> rechercherArticles(int offsetResultats, int nombreResultatsMax, String attributTri, boolean triAscendant, 
    		Long idSport, Long idCompetition, Long idAuteur, String titre, Date dateMinimum, Float noteMinimum, Integer nombreCommentairesMinimum) {
    	
		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<Article> criteria = builder.createQuery(Article.class);
		Root<Article> articleRoot = criteria.from(Article.class);

		criteria.select(articleRoot);
		
		Predicate condition = genererCondition(articleRoot, idSport, idCompetition, idAuteur, dateMinimum, noteMinimum, nombreCommentairesMinimum);
		if (condition != null) {		
			criteria.where(condition);
		}
		if (attributTri != null) {
			if (triAscendant)
				criteria.orderBy(builder.asc(articleRoot.get(attributTri)));
			else
				criteria.orderBy(builder.desc(articleRoot.get(attributTri)));
		}
		
		TypedQuery<Article> query = em.createQuery(criteria);
		query.setFirstResult(offsetResultats);
		query.setMaxResults(nombreResultatsMax);
		
		return query.getResultList();
    	
    }
    
    /**
     * Génère un prédicat à partir de différents paramètres
     * @param articleRoot
     * @param idCompetition
     * @param idAuteur
     * @param dateMinimum
     * @param noteMinimum
     * @param nombreCommentairesMinimum
     * @return prédicat généré
     */
    private Predicate genererCondition(Root<Article> articleRoot, Long idSport, Long idCompetition, Long idAuteur, Date dateMinimum, Float noteMinimum, Integer nombreCommentairesMinimum) {

    	CriteriaBuilder builder = em.getCriteriaBuilder();
		
    	Predicate condition = null;

    	if (idSport != null) {
    		Predicate conditionSport = builder.equal(articleRoot.get("sport"), idSport);
    		condition = conditionSport;
    	}	
    	if (idCompetition != null) {
    		Predicate conditionCompetition = builder.equal(articleRoot.get("competition"), idCompetition);
    		condition = condition != null ? builder.and(condition, conditionCompetition) : conditionCompetition;
    	}		
    	if (idAuteur != null) {
    		Predicate conditionAuteur = builder.equal(articleRoot.get("auteur"), idAuteur);
    		condition = condition != null ? builder.and(condition, conditionAuteur) : conditionAuteur;
    	}
    	if (dateMinimum != null) {
    		Predicate conditionDate =  builder.greaterThanOrEqualTo(articleRoot.get("date").as(Date.class), dateMinimum);
    		condition = condition != null ? builder.and(condition, conditionDate) : conditionDate;
    	}
    	if (noteMinimum != null) {
    		Predicate conditionNote = builder.greaterThanOrEqualTo(articleRoot.get("noteMoyenne").as(Float.class), noteMinimum);
    		condition = condition != null ? builder.and(condition, conditionNote) : conditionNote;
    	}
    	if (nombreCommentairesMinimum != null) {
			Predicate conditionNombreCommentaires = 
				builder.greaterThanOrEqualTo(articleRoot.get("nombreCommentaires").as(Integer.class), nombreCommentairesMinimum);
			condition = condition != null ? builder.and(condition, conditionNombreCommentaires) : conditionNombreCommentaires;
    	}
    
    	return condition;
    
    }
    
    /**
     * Recherche par mot-clé optimisée
     * @param offsetResultats
     * @param nombreResultatsMax
     * @param requeteTexte mots clés utilisés pour la recherche
     * @return liste d'articles contenant les mots clés
     */
    
    public List<Article> rechercherArticlesParTexte(int offsetResultats, int nombreResultatsMax, String requeteTexte) {
    	
    	org.apache.lucene.search.Query queryTexte = null;
    	

    	// Création du fullTextEntityManager  
    	FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);  
    	
    	try {
	    	// Requête pour la recherche dans le texte de l'article
           	if (requeteTexte != null && !requeteTexte.equals("")) {
		    	String[] fields = new String[] {"texte", "titre"};  
		    	// Récupération de l'analyseur associé à la classe Article
		    	// Cet analyseur possède un filtre permettant une tolérance à l'erreur
		    	Analyzer articleAnalyzer = fullTextEntityManager.getSearchFactory().getAnalyzer(Article.class);  
		    	MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_CURRENT, fields, articleAnalyzer); 
		    	
		    	try {  
			    	queryTexte = parser.parse(requeteTexte);  
		    	} catch (ParseException e) {  
		    		e.printStackTrace();
		    	} 
           	}
           	
           	Criteria criteria = null;
           	// Définition de différents critères
//           	SessionFactory factory = new Configuration().configure().buildSessionFactory();
//           	Session session = factory.openSession();
//
//           	Criteria criteria = session.createCriteria(Article.class);
//        	if (idSport != null) {
//        		criteria.add(Restrictions.eq("sport", idSport));
//	    	}	
//	    	if (idCompetition != null) {
//	    		criteria.add(Restrictions.eq("competition", idCompetition));
//	    	}		
//	    	if (idAuteur != null) {
//	    		criteria.add(Restrictions.eq("auteur", idAuteur));
//	    	}
//	    	if (dateMinimum != null) {
//	    		criteria.add(Restrictions.ge("date", dateMinimum));
//	    	}
//	    	if (noteMinimum != null) {
//	    		criteria.add(Restrictions.ge("noteMoyenne", noteMinimum));
//	    	}
//	    	if (nombreCommentairesMinimum != null) {
//	    		criteria.add(Restrictions.ge("nombreCommentaires", nombreCommentairesMinimum));
//	    	}
//           	
//           	criteria.setFirstResult(offsetResultats);
//           	criteria.setMaxResults(nombreResultatsMax);
//           	
//    		if (attributTri != null) {
//    			if (triAscendant)
//    				criteria.addOrder(Order.asc(attributTri));
//    			else
//    				criteria.addOrder(Order.desc(attributTri));
//    		}
	    	
	    	// Requête finale prenant en compte la requête dans le texte et les critères ci-dessus
	    	
	    	javax.persistence.Query persistenceQuery = fullTextEntityManager.createFullTextQuery(queryTexte, Article.class).setCriteriaQuery(criteria);  

	    	// Récupération des résultats  
	    	persistenceQuery.setFirstResult(offsetResultats);
	    	persistenceQuery.setMaxResults(nombreResultatsMax);
	    	List resultats = persistenceQuery.getResultList();
	    	return resultats.isEmpty() ? new ArrayList<Article>() : resultats;  
    	} 
    	catch (Exception e) {
    		e.printStackTrace();
    		return new ArrayList<Article>();
    	}
    	
    }
	

    public int getNombreResultatsRechercheArticlesParTexte(String requeteTexte) {
    	
    	org.apache.lucene.search.Query queryTexte = null;
    	

    	// Création du fullTextEntityManager  
    	FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);  
    	
    	try {
	    	// Requête pour la recherche dans le texte de l'article
           	if (requeteTexte != null && !requeteTexte.equals("")) {
		    	String[] fields = new String[] {"texte", "titre"};  
		    	Analyzer articleAnalyzer = fullTextEntityManager.getSearchFactory().getAnalyzer(Article.class);  
		    	MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_CURRENT, fields, articleAnalyzer); 
		    	
		    	try {  
			    	queryTexte = parser.parse(requeteTexte);  
		    	} catch (ParseException e) {  
		    		e.printStackTrace();
		    	} 
           	}
           	
           	Criteria criteria = null;
	    	
	    	// Requête finale prenant en compte la requête dans le texte et les critères ci-dessus
	    	
	    	javax.persistence.Query persistenceQuery = fullTextEntityManager.createFullTextQuery(queryTexte, Article.class).setCriteriaQuery(criteria);  

	    	// Récupération du nombre de résultats  
	    	return persistenceQuery.getResultList().size();  
    	} 
    	catch (Exception e) {
    		e.printStackTrace();
    		return 0;
    	}	
    }
    
}
